
#include <boost/program_options.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/filesystem.hpp>
#include <iostream>
#include <iterator>
#include <fstream>
#include <sstream>

#include <kodes.h>

using namespace std;


extern const char *SignaroNomoj[];


// LegoScribilo&, 
class Konvertado
{
public:
  Konvertado(LegoKonverto&, ScriboKonverto&);
  void komencu();
  virtual bool progreso(float);
private:
  LegoKonverto &lg_;
  ScriboKonverto &sc_;
};


Konvertado::Konvertado(LegoKonverto &lg, ScriboKonverto &sc)
: lg_(lg),
  sc_(sc)
{
}

void Konvertado::komencu()
{
  for (;;)
  {
    wchar_t c = lg_.doni();
    if (!c)
      break;
    sc_.meti(c);
    progreso(lg_.progreso());
  }
}

bool Konvertado::progreso(float)
{
  return true;
}



int main(int ac, char* av[])
{
#ifdef _DEBUG
  extern void rut_tests();
  rut_tests();
#endif

  cout << "Korvertilo de Esperanto signaroj." << endl;

  try {
    namespace po = boost::program_options;

    po::options_description desc("Allowed options");
    desc.add_options()
      ("help", "produce help message")
      ("file", po::value< vector<string> >(), "one or more file for encoding change")
      ("from", po::value<string>(), "source encoding")
      ("to", po::value<string>(), "destination encoding")
      ("bom", "save utf with BOM")
      ;

    po::positional_options_description p;
    p.add("file", -1);

    po::variables_map vm;
    po::store(po::command_line_parser(ac, av).options(desc).positional(p).run(), vm);
    po::notify(vm);

    if (vm.count("help"))
    {
      cout << desc << "\n";
      return 1;
    }

    if (vm.count("from")==0 || vm.count("to")==0)
      throw runtime_error("Specify in and out encodings!");

    Signaro inEncoding = S_LAST, outEncoding = S_LAST;

    for (size_t i=0; i<S_LAST; i++)
    {
      if (0 == stricmp(vm["from"].as<string>().c_str(), SignaroNomoj[i]))
        inEncoding = (Signaro)i;
      if (0 == stricmp(vm["to"].as<string>().c_str(), SignaroNomoj[i]))
        outEncoding = (Signaro)i;
    }

    LegoKonverto *lk = LegoKonverto::getBySignaro(inEncoding);
    boost::scoped_ptr<LegoKonverto> LegKonv(lk);

    ScriboKonverto *sk = ScriboKonverto::getBySignaro(outEncoding);
    boost::scoped_ptr<ScriboKonverto> ScrKonv(sk);

    if (!lk || !sk)
    {
      cerr << "Wrong encoding, allowed only: ";
      for (size_t i=0; i<S_LAST; i++)
        cerr << SignaroNomoj[i] << " ";
      cerr << endl;
      return 1;
    }

    if (vm.count("bom"))
      sk->writeBom(true);

    if (vm.count("file"))
    {
      using namespace ::boost::filesystem;
      cout << "Start conversion from " << SignaroNomoj[inEncoding] << " to " << SignaroNomoj[outEncoding] << endl;

      const vector<string> &files = vm["file"].as< vector<string> >();

      for (size_t i=0; i<files.size(); i++)
      {
        path inputFile (files[i]);
        path tmpFile = inputFile;
        tmpFile.remove_filename();
        tmpFile /= unique_path();

        cout << "  file: " << inputFile.string().c_str() << " ...";

        try
        {
          {
//            boost::scoped_ptr<LegoScribilo> leScIlo(new DosieroLegoScribilo(inputFile.string().c_str(), tmpFile.string().c_str()));
//            if (!leScIlo->good())
//              throw runtime_error("Can't open file!");

            ifstream rdFile(inputFile.string(), ios_base::in | ios_base::binary);
            if (!rdFile.is_open())
              throw runtime_error("Can not open input file");
            LegKonv->setStream(&rdFile);

            ofstream wrFile(tmpFile.string(), ios_base::out | ios_base::binary);
            if (!wrFile.is_open())
              throw runtime_error("Can not open temp output file");
            ScrKonv->setStream(&wrFile);

            Konvertado konv(*LegKonv, *ScrKonv);
            konv.komencu();
          }

          remove(inputFile);
          rename(tmpFile, inputFile);

          cout << " ok" << endl;
        }
        catch (const exception &eraro)
        {
          cerr << "error: " << eraro.what() << endl;
        }

      }
    }
  }
  catch(exception& e)
  {
    cerr << "error: " << e.what() << "\n";
    return 1;
  }
}




